home *** CD-ROM | disk | FTP | other *** search
- /*******************************************************************************
-
- File: disk.c
-
- Contains:
- Contains most of the code for a SCSI disk driver.
- This file is part of the Example SCSI Driver
-
- Warnings
- Copying this driver line for line and shipping it is bad
- SCSI Karma. Remember the old APDA driver? Well this isn't
- that bad, but you can do better. This is only an example.
-
- Features
- Supports both Original and SCSI Manager 4.3
- Loads and installs with either Manager
- Transitions from original to 4.3 correctly
- Multiple Partition Support
- New Driver Rules support
-
- Known Issues
- There is no error handling other than retries.
- No Request Sense
- No bad block re-mapping
- No support for older drives which require 1,511 TIBs.
- The Mac Plus is not supported.
- No support for hardware- or firmware-specific device
- features.
-
-
- Copyright: © 1986-1994 by Apple Computer, Inc., all rights reserved.
-
-
- *******************************************************************************/
-
-
-
- #include <types.h>
- #include <memory.h>
- #include <OSUtils.h>
- #include <OSEvents.h>
- #include <events.h>
- #include <files.h>
- #include <devices.h>
- #include <disks.h>
- #include <errors.h>
- #include <traps.h>
- /**
- ** "scsi.h" in this folder is a preliminary version of the standard header.
- ** When a final version is released on ETO, you should replace the following
- ** #include "scsi.h" with #include <scsi.h>
- **/
- #ifndef _SCSIAtomic
- #define _SCSIAtomic 0xA089
- #endif
- #include "scsi.h"
-
- #include "disk.h"
- #include "DriverGestalt.h" /* This file will eventually be rolled into devices.h */
-
- #include "UnitNtryCnt.h"
-
- #include "Version.r"
-
-
- /* main driver variables */
-
- Boolean newSCSIPresent; /* flag set if SCSI Manager 4.3 is present */
- Boolean forceSync; /* force new SCSI Manager request to be synchronous */
- unsigned char numHFS; /* number of HFS partitions seen that need disk insert events. */
- Boolean unusedB1;
-
- /* Per-drive variables */
- SCSIInstr tib[6]; /* largest size of TIB needed (10 bytes/instr) */
- unsigned long scsiID; /* scsiID for this drive */
- DeviceIdent devIdent; /* The device id (bus,id,lun) for this drive */
- IOParam *gioPB; /* Remember the current parameter block and dce for */
- AuxDCE *gdce; /* the benefit of the async callback routine */
- SCSI_IO *scPB; /* Parameter block for async SCSI - one per drive */
- unsigned long thisTime; /* The amount we are transferring with this transaction */
- unsigned long remaining; /* The amount left over (bytes) if we needed to break up a transaction */
- unsigned long asyncRetries; /* Used for retrying from a completion routine */
- unsigned long standardFlags; /* standard set of flags for scsiFlags field */
- unsigned short standardIOFlags; /* standard set of flags for scsiIOFlags field */
- Boolean initiateSyncData; /* True if we need to initiate synchronous */
- Boolean unusedB2;
-
- /* Error Handling variables */
-
- unsigned char *originalBuffer; /* The original parameters from the prime call */
- unsigned long originalLength; /* so we can retry the command when we are done */
- unsigned long originalBlock; /* fixing the bad block */
- short originalDir;
-
- /* Per-volume variables. One for every partition. */
- struct pervolume {
- /* the DrvSts record from I.M. 4 (superset of the drive queue structure) */
-
- /*
- ** This whole section is a rather egregious hack. Actually only part of the DrvQEl
- ** structure is in the DrvSts structure. As it turns out the drvQSz fields overlap
- ** with the needsFlush and the diskErrs fields. It is the same as a DrvSts2 struct
- ** which doesn't appear to be used by anybody. Fortunatly the needsFlush and diskErrs
- ** fields aren't used either. Another bogus aspect to this is the way in which it
- ** is copied into the status record passed into the status call. The code takes
- ** the address of the track field and copies a #defined amount of data from there into
- ** the user's buffer. This should be done by defining this stuff as a DrvSts structure
- ** and doing a structure copy or something like that. Nevertheless it does work as is.
- */
-
- short track; /* 00: 0, unused */
- char writeProt; /* 02: bit 7=1 if write-protected */
- char diskInPlace; /* 03: 8, for non-ejectable disk */
- char installed; /* 04: 1, for drive installed */
- char sides; /* 05: 0, unused (same as HD20) */
- DrvQEl mydqel; /* 06: the normal drive queue element */
- /* miscellaneous per-volume data */
- unsigned short mydrvnum; /* 16: drive number for this volume */
- unsigned long partoffset; /* 18: phys. offset of data partition */
- unsigned long curoffset; /* 1c: 0 = physical mapping, else 'partoffset' */
- unsigned long partblks; /* 20: number of blocks in partition */
- unsigned short mountthispart; /* 24: 1 if a disk insert event needs to be posted yet. */
- unsigned short unusedS; /* 26: */
- Ptr verifyBfr; /* 28: ptr to buffer to read into for verified writes */
- /* 2c: */
- } pv[MAXPARTITIONS];
-
- /* End of global data */
-
-
-
- /* All the routines declared in this file */
- short getRefNum(DeviceIdent DI);
- void registerDriver(DeviceIdent DI, short refnum);
- void removeDriver(DeviceIdent DI);
- void NewSCSIPB(long length);
- void ConfigureForBusOptions(void);
- short FindAnEmptyVolumeStorage(void);
- unsigned char ConvertToUC(unsigned char theCharacter);
- short GetPartitionType(Partition *thePartition);
- OSErr dropen(AuxDCE *dce, short mountflag, DeviceIdent DI);
- OSErr drclose(AuxDCE *dce, IOParam *iopb);
- OSErr prime(AuxDCE *dce, IOParam *iopb);
- short BlockSCSIXfer(unsigned long wrflag, unsigned long count, unsigned long sector, unsigned char *buf, CallbackProc compRoutine);
- short doNewSCSI(unsigned long wrflag, unsigned long count, unsigned long sector, unsigned char *buf, CallbackProc compRoutine);
- OSErr completeIO(SCSI_IO *scPB);
- Ptr getDCE(SCSI_IO *scPB);
- OSErr control(AuxDCE *dce, CntrlParam *iopb);
- unsigned char ByteToBCD(unsigned char c);
- OSErr status(AuxDCE *dce, CntrlParam *ioPtr);
- OSErr blockio(unsigned short wrflag, unsigned long count, unsigned long sector, unsigned char *buf);
- short scsiop(char *cmd, unsigned short cmdlen, struct SCSIInstr *op_tib, unsigned short wr, unsigned short blind, unsigned long time);
- void initcommand(unsigned char cmdbuffer[], unsigned char cmd, short len);
- void Clear(char *where, short len);
-
- Boolean TrapAvailable(short theTrap); /* Inside Mac VI test for SCSI 4.3 trap */
-
- /* External routines */
- extern char *A5Alloc(void);
- extern char *A5Setup(char *newA5);
- extern pascal void drvrCompIO( SCSI_IO * );
-
-
-
-
- /* getRefNum -
- ** Decides what our reference number should be in the new (SCSI 4.3) environment (called from disk.a)
- **
- ** The installation code sets bit #29 in the DeviceIdent if it was entered through the async SCSI Entry
- ** point. This code uses that bit to decide what our reference number should be.
- **
- ** If the device is visible to SCSISelect we use the fixed unit table entry
- ** otherwise we walk the unit table to find an empty spot. This is to allow
- ** older SCSI utilites like SCSI Probe to keep working. If we didn't use the
- ** fixed unit table entry SCSI Probe might think there was no driver loaded and
- ** try to load one.
- */
- short
- getRefNum( DeviceIdent DI)
- {
- unsigned long count;
- short refnum;
- unsigned char flags;
- SCSIGetVirtualIDInfoPB dIdent;
- unsigned short myUnitNtryCnt = LMGetUnitEntryCount();
-
- flags = DI.diReserved;
- DI.diReserved = 0; /* no longer want the extra bits in the DeviceIdent */
-
- if ( !(flags & 0x20) )
- return( IDTOREFNUM(*(long *)&DI) ); /* Old style driver - return old-slot dRefNum */
-
- /* Find out if this is an old call bus and if we are going to */
- /* use a static slot in the unit table */
-
- Clear((char *)&dIdent,sizeof(dIdent));
- dIdent.scsiOldCallID = DI.targetID;
- dIdent.scsiFunctionCode = SCSIGetVirtualIDInfo;
- dIdent.scsiCompletion = nil;
- dIdent.scsiPBLength = sizeof(SCSIGetVirtualIDInfoPB);
- SCSIAction((SCSI_PB *) &dIdent);
- if( dIdent.scsiExists && DI.bus == dIdent.scsiDevice.bus )
- return(IDTOREFNUM(DI.targetID)); /* Use the static slot in the unit table */
-
- for( count = 48; count < myUnitNtryCnt ; count++ ) {
- refnum = ~count;
- if( GetDCtlEntry(refnum) == 0 )
- return(refnum); /* Use the first empty spot we find */
- }
-
- /* If we got through the Unit Table without finding an empty slot, we must
- expand it. There is sample code available from DTS showing how to do this */
-
- return(0);
- }
-
- /*
- ** Registers a driver with the SCSI Manager
- ** and fills in the AuxDCE info for Startup Disk
- **
- ** This only happens if we are running with the 4.3 interface.
- */
- void
- registerDriver( DeviceIdent DI, short refnum )
- {
- SCSIDriverPB myPB;
- SCSIBusInquiryPB inqPB; /* Info about the scsi bus */
- PackedDevID PDevID;
-
- /*
- ** First we need to do a bus inquiry and determine the slot and srsrc
- ** number for the SIM we are using. We take these values and plug them
- ** into our extended DCE field where startup disk can find them. We
- ** also fill in an encoded (8 bits total) version of our ID and LUN.
- */
- Clear((char *)&inqPB,sizeof(inqPB));
- inqPB.scsiDevice = DI;
- inqPB.scsiFlags = 0;
- inqPB.scsiFunctionCode = SCSIBusInquiry;
- inqPB.scsiCompletion = nil;
- inqPB.scsiPBLength = sizeof(SCSIBusInquiryPB);
- SCSIAction( (SCSI_PB *) &inqPB ); /* Get slot and srsrc info for this bus */
-
- gdce->dCtlSlot = inqPB.scsiHBAslotNumber;
- gdce->dCtlSlotId = inqPB.scsiSIMsRsrcID;
- PDevID.targetID = DI.targetID;
- PDevID.LUN = DI.LUN;
- gdce->dCtlExtDev = *(char *)& PDevID;
-
- /* And the other thing we need to do is tell the SCSI Manager we are
- ** here so that other applications can find us again without relying
- ** on the fixed unit table entries
- */
- Clear((char *)&myPB,sizeof(myPB));
- myPB.scsiDevice = DI;
- myPB.scsiDriver = refnum;
- myPB.scsiFunctionCode = SCSICreateRefNumXref;
- myPB.scsiCompletion = nil;
- myPB.scsiPBLength = sizeof(SCSIDriverPB);
- SCSIAction((SCSI_PB *)&myPB);
- }
-
- /*
- ** De-Registers a driver with the SCSI Manager
- **
- ** This function merely calls RemoveRefNumXref to let the SCSI Manager
- ** know we aren't controlling this Device ID anymore.
- */
- void
- removeDriver( DeviceIdent DI )
- {
- SCSIDriverPB myPB;
-
- if( !newSCSIPresent )
- return;
-
- Clear((char *)&myPB,sizeof(myPB));
- myPB.scsiDevice = DI;
- myPB.scsiFunctionCode = SCSIRemoveRefNumXref;
- myPB.scsiCompletion = nil;
- myPB.scsiPBLength = sizeof(SCSIDriverPB);
- SCSIAction((SCSI_PB *)&myPB);
- }
-
- /*
- ** This function just fills in those parameter block values which don't
- ** change from call to call. This saves a little time on each transaction.
- */
- void
- NewSCSIPB( long length )
- {
- scPB->scsiPBLength = length;
- scPB->scsiDevice = devIdent;
- scPB->scsiFunctionCode = SCSIExecIO;
- scPB->scsiDataType = scsiDataBuffer;
- }
-
- /*
- ** This code normally gets executed twice. Once on initialization and once when
- ** the first accRun tick occurs. It sets up a the standard and IO flags to support
- ** whatever features of the SIM interest us. (like Fast Synchronous). It also would
- ** normally be the place where features which are known not to work with a particular
- ** drive would be stripped out.
- */
- void
- ConfigureForBusOptions(void)
- {
- SCSIBusInquiryPB inqPB;
-
- Clear((char *) &inqPB, sizeof(inqPB));
- inqPB.scsiDevice = devIdent;
- inqPB.scsiFlags = 0;
- inqPB.scsiCompletion = nil;
- inqPB.scsiPBLength = sizeof(SCSIBusInquiryPB);
-
- inqPB.scsiFunctionCode = SCSIBusInquiry; SCSIAction( (SCSI_PB *) &inqPB );
-
- /* Defaults */
-
- standardFlags = 0;
- standardIOFlags = 0;
- initiateSyncData = false;
-
- /*
- ** Check to see if sync data is supported and, if so, whether we want to use it or not
- ** Note that we will only try and use FAST synchronous for this driver. IMHO synchronous
- ** isn't worth the effort unless you are going Fast
- */
-
- if( inqPB.scsiFlagsSupported & scsiInitiateSyncData) {
- if( inqPB.scsiFeatureFlags & scsiBusFastSCSI ) {
- initiateSyncData = true;
- standardIOFlags |= scsiRenegotiateSense;
- }
- }
-
- /* Strip away feature flags not supported on our bus */
-
- standardFlags &= inqPB.scsiFlagsSupported;
- standardIOFlags &= inqPB.scsiIOFlagsSupported;
-
- standardFlags |= scsiDisableAutosense; /* This driver doesn't look at sense data */
- }
-
-
- /*
- ** FindAnEmptyVolumeStorage - Routine that searches through the pv array for a free slot.
- **
- ** Returns - if there's a free slot, the index.
- ** if there's no free slot, a -1.
- */
-
- short
- FindAnEmptyVolumeStorage(void)
- {
- short i;
-
- for( i = 0; i < MAXPARTITIONS; i++)
- if( (pv[i].mydrvnum == (unsigned short) NODRVNUM) )
- return (i);
- /* All of the slots are taken. */
- return (-1);
- }
-
-
- /*
- ** ConvertToUC - Converts lower case characters to upper case characters.
- */
-
- unsigned char ConvertToUC (unsigned char theCharacter)
- {
- if( (theCharacter >= 'a') && (theCharacter <= 'z'))
- return (theCharacter & 0xdf);
- else
- return (theCharacter);
- }
-
-
- /*
- ** GetPartitionType - Used to determine what type of partition was gotten.
- **
- ** Input - thePartition, a pointer to a partition.
- **
- ** Returns - ISMACDRIVER when partition is of type Apple_Driver43 for the Macintosh.
- ** ISHFS when partition is of type Apple_HFS.
- ** ISNOTADDTOQUEUE when partition is of types Apple_partition_map, Apple_Free or Apple_Scratch.
- */
- short
- GetPartitionType (Partition *thePartition)
- {
- short i,j;
- static char macintoshPartitionName[] = "MACINTOSH";
- char *knownPartitionTypePtr;
- static char *knownPartitionTypes[] = {
- "APPLE_DRIVER43",
- "APPLE_HFS",
- "APPLE_PARTITION_MAP",
- "APPLE_FREE",
- "APPLE_SCRATCH",
- };
-
- /* First check to see if we recognize one of the known partitions. */
- for( j = POS_BEGIN; j <= POS_END; j++) {
- i = 0;
- knownPartitionTypePtr = knownPartitionTypes[j];
- while (knownPartitionTypePtr[i] == ConvertToUC (thePartition->pmParType[i])) {
- if( knownPartitionTypePtr[i++] == '\0') {
- /* We found one that we recognize. */
- switch (j) {
- case POS_APPLE_HFS:
- return (ISHFS);
- case POS_APPLE_DRIVER:
- return (ISMACDRIVER);
- default:
- /* We must have found one of the ones that can't be mounted. */
- return (ISNOTADDTOQUEUE);
- }
- }
- }
- }
- /* Wasn't something we recognized */
- return (ISNOTADDTOQUEUE);
- }
-
- /*
- ** The open routine... (Gets called from disk.a.)
- **
- ** This routine sets up the A5 world for the driver, scans for partitions,
- ** installs drive queue elements and decides which SCSI Manager to use.
- */
- OSErr
- dropen(AuxDCE *dce, short mountflag, DeviceIdent DI)
- {
- register struct pervolume * pvptr;
- char *mya5, *olda5;
- THz curzone;
- unsigned short dn, id;
- Boolean founddrvr;
- short err;
- unsigned long i, lastentry;
- DrvQEl *qel;
- short partitionType;
- short volumeStorageIndex;
- SCSIBusInquiryPB inqPB; /* Info about the scsi bus */
- SCSIGetVirtualIDInfoPB dIdent;
- union {
- Partition pt;
- char data[BLKSIZE];
- } buf;
-
-
- if( dce->dCtlStorage != nil ) { /* if we already have a global A5 world */
- olda5 = A5Setup((char *)dce->dCtlStorage);
- } else { /* no A5 globals, so set them up */
- curzone = GetZone();
- SetZone(SystemZone()); /* globals go in the system heap */
- mya5 = A5Alloc();
- SetZone(curzone);
- if( mya5 == NULL) /* can't allocate space for globals */
- return(-1);
- A5Init(mya5);
- dce->dCtlStorage = (Handle) mya5;
- olda5 = A5Setup(mya5);
-
- numHFS = 0; /* No HFS partitions have been seen yet. */
-
- /* Show that all of the per-volume variables are currently available. */
- for( i = 0; i < MAXPARTITIONS; i++) {
- pv[i].mydrvnum = NODRVNUM;
- }
- }
-
- gdce = dce; /* Remember the dce for callback routine */
-
- dce->dCtlFlags |= DriverGestalt_Enable_Mask; /* Flag clients that know about Driver Gestalt */
-
- newSCSIPresent = false;
- scPB = nil;
-
- if( !(*(unsigned long *)&DI & 0x20000000) ) { /* Old style call to driver (offset 0) */
- *(unsigned long *)&devIdent = -1;
- scsiID = id = (*(unsigned long *)&DI) & 0xFF;
- }
- else { /* New style call to driver (offset 8) */
- devIdent = DI;
- devIdent.diReserved = 0;
- scsiID = id = DI.targetID;
- }
-
- /* If we have the new SCSI Mgr then see if we can use it and if so, prepare to do so */
-
- if(TrapAvailable(SCSIATOM)) {
- /* The trap is present and safe to use */
-
- /* if we were called with a SCSI ID only, get a DeviceIdent from GetVirtualIDInfo */
-
- if( !(*(unsigned long *)&DI & 0x20000000) ) { /* Old style call to driver */
- Clear((char *)&dIdent,sizeof(dIdent));
- dIdent.scsiOldCallID = (*(unsigned long *)&DI) & 0xFF;
- dIdent.scsiFunctionCode = SCSIGetVirtualIDInfo;
- dIdent.scsiCompletion = nil;
- dIdent.scsiPBLength = sizeof(SCSIGetVirtualIDInfoPB);
- SCSIAction((SCSI_PB *) &dIdent);
- if( dIdent.scsiExists )
- devIdent = dIdent.scsiDevice;
- }
-
- /* If our device exists on a new-API capable bus, alloc a PB and get ready for new-API */
-
- if( *(long *)&devIdent != -1 ) {
-
- Clear((char *)&inqPB,sizeof(inqPB));
- inqPB.scsiDevice = devIdent;
- inqPB.scsiFlags = 0;
- inqPB.scsiFunctionCode = SCSIBusInquiry;
- inqPB.scsiCompletion = nil;
- inqPB.scsiPBLength = sizeof(SCSIBusInquiryPB);
- SCSIAction( (SCSI_PB *) &inqPB ); /* Get the size of a SCSI_IO for this bus */
-
- if( inqPB.scsiResult == noErr && inqPB.scsiVersionNumber == scsiVERSION ) {
-
- if( (scPB = (SCSI_IO *)NewPtrSysClear(inqPB.scsiIOpbSize)) != 0 ) {
- newSCSIPresent = true;
- registerDriver( devIdent, dce->dCtlRefNum );
- NewSCSIPB( inqPB.scsiIOpbSize);
- }
- }
- }
- }
- if( newSCSIPresent == false ) {
- *(unsigned long *)&devIdent = -1; /* Guaranteed to NOT be a devIdent - the real one is generated in the prime
- routine */
- tib[0].scOpcode = scInc; /* Build the re-usable parts of the TIB */
- tib[0].scParam2 = BLKSIZE;
- tib[1].scOpcode = scLoop;
- tib[1].scParam1 = -1*sizeof(struct SCSIInstr);
- tib[2].scOpcode = scStop;
- }
-
- founddrvr = false;
- lastentry = 1;
-
- /*
- ** Walk through the partition map and look for mountable partitions.
- **
- ** If we had different devices we needed to support we could store
- ** device specific information in the driver partition map entry
- ** and setup the standard and IO flags and other globals accordingly.
- */
- for( i = 1; i <= lastentry; i++) {
- err = BlockSCSIXfer( READ_F, 1, i, (unsigned char *)&buf.pt, nil);
- if( err )
- continue;
- if( buf.pt.pmSig != pMapSIG)
- continue;
- if( i == 1) /* save the number of partition map entries */
- lastentry = buf.pt.pmMapBlkCnt;
- partitionType = GetPartitionType(&buf.pt);
-
- switch (partitionType) {
-
- case ISMACDRIVER:
- /* You can use the driver partition map entry to store information about the drive such as
- whether it supports synchronous, disconnect/reconnect etc.
- */
- founddrvr = true; /* We found the driver partition. */
- break;
-
- case ISHFS:
- /* Check if there's any more room to handle another partition. */
- volumeStorageIndex = FindAnEmptyVolumeStorage();
- if( volumeStorageIndex == -1)
- break;
- pvptr = &pv[volumeStorageIndex]; /* Saves lots of array indexing. */
- /* Get a drive number for this drive. */
- dn = 8; /* Avoid any possible conficts with built-in drive numbers. */
- searchqueue:
- qel = (DrvQEl *)((struct QHdr *)DRVQHDR)->qHead; /* start at drive queue head */
- while (qel) {
- if( dn == qel->dQDrive) { /* This drivenum is already in use. */
- dn++; /* Try another number. */
- goto searchqueue; /* Start at dq head again. */
- } else {
- qel = (DrvQEl *)qel->qLink; /* otherwise goto next qel */
- }
- }
- /* set up DrvSts structure according to IM Vol. 4 */
- pvptr->track = 0;
- pvptr->writeProt = (buf.pt.pmPartStatus & 0x20) ? 0 : 0x80; /* Look at the writable bit in partition status */
- pvptr->diskInPlace = 8; /* mark disk as non-ejectable */
- pvptr->installed = 1;
- pvptr->sides = 0;
- pvptr->mydqel.qType = 1;
- pvptr->mydqel.qLink = (QElemPtr)0;
- pvptr->mountthispart = 0; /* Show that this partition down not need to be mounted. */
- pvptr->mydqel.dQFSID = 0; /* FSID of 0 is HFS */
- if( (partitionType == ISHFS) && mountflag) {
- numHFS++; /* We found an HFS partition. */
- pvptr->mountthispart = 1; /* Show that this partition needs to be mounted. */
- }
- pvptr->mydqel.dQDrvSz = buf.pt.pmDataCnt; /* low word */
- pvptr->mydqel.dQDrvSz2 = buf.pt.pmDataCnt>>16; /* high word */
- pvptr->curoffset = pvptr->partoffset = buf.pt.pmPyPartStart + buf.pt.pmLgDataStart;
- pvptr->partblks = buf.pt.pmDataCnt;
- pvptr->mydrvnum = dn;
- AddDrive(dce->dCtlRefNum, dn, &pvptr->mydqel);
- if( mountflag && pvptr->mountthispart )
- PostEvent(diskEvt, dn);
- break;
-
- case ISNOTADDTOQUEUE:
- /* We can simply ignore this partition. */
- break;
- }
- }
-
- if( !founddrvr ) { /* Can't locate proper entry for driver */
- err = -2;
- }
- else {
- err = 0; /* able to open everything OK */
- if( newSCSIPresent )
- ConfigureForBusOptions();
- }
-
- if( err) { /* remove DrvQEls */
- qel = (DrvQEl *)((struct QHdr *)DRVQHDR)->qHead; /* start at drive queue head */
- while (qel) {
- if( dce->dCtlRefNum == qel->dQRefNum) { /* Is this drive for our driver? */
- Dequeue ((QElemPtr)qel, (QHdrPtr)DRVQHDR);
- }
- qel = (DrvQEl *)qel->qLink;
- }
- }
-
- A5Restore(olda5); /* restore the old A5 */
-
- return(err);
- }
-
-
- /*
- ** The close routine.
- **
- ** Pretty simple - SCSI drivers should implement this. It makes
- ** life a lot easier for utilites which need to remove older drivers.
- */
- OSErr
- drclose(AuxDCE *dce, IOParam *iopb)
- {
- #pragma unused(iopb)
- struct pervolume *pvptr;
- THz curzone;
- short i;
-
- removeDriver( devIdent ); /* Tell the SCSI Manager we are going away */
-
- /* Remove any of the drive queue entries associated with this drive. */
- for( i = 0; i < MAXPARTITIONS; i++) {
- pvptr = &pv[i]; /* Saves lots of array indexing. */
- Dequeue ((QElemPtr)&(pvptr->mydqel), (QHdrPtr)DRVQHDR);
- pvptr->mydrvnum = NODRVNUM; /* Free up the space in the per volume array. */
- if( (pvptr->mountthispart) == 1)
- numHFS--; /* This partition wasn't mounted yet, it now never will be. */
- }
-
- //!!! I don't know if we need this stupid change zones stuff for a dispose.
- curzone = GetZone(); /* set the proper zone... */
- SetZone(SystemZone());
- A5Dispose(dce->dCtlStorage); /* dispose of the driver's A5 world */
- SetZone(curzone);
- return(0);
- }
-
-
- /*
- ** The prime routine...
- **
- ** Handles both asynchronous and synchronous requests identically. The
- ** Device Manager will do any necessary sync waits for synchronous requests.
- ** Immediate requests are done synchronously.
- */
- OSErr
- prime(AuxDCE *dce, IOParam *iopb)
- {
- register struct pervolume *pvptr;
- short err, wrflag;
- unsigned long startblk, numblks;
- short i;
- short retries;
- Boolean success = 0;
-
- wrflag = ((unsigned char)iopb->ioTrap == 3); /* 3 is the Write call */
-
- /* calculate the starting block and block count (divide by 512) */
- startblk = dce->dCtlPosition >> 9;
- numblks = iopb->ioReqCount >> 9;
-
- /* Search for the drive number and set up pvptr to speed up the array access. */
- for( i = 0; i < MAXPARTITIONS; i++) {
- pvptr = &pv[i];
- if( pvptr->mydrvnum == iopb->ioVRefNum )
- break;
- }
-
- /* Make sure that we have a valid drive number. */
- if( i == MAXPARTITIONS) {
- err = nsDrvErr;
-
- } else if( ((iopb->ioReqCount & (BLKSIZE-1)) != 0) ||
- ((pvptr->curoffset != 0) && (startblk+numblks > pvptr->partblks))) {
- /* not a multiple of 512 bytes or block out of range of partition */
- err = paramErr;
-
- } else {
-
- for( retries = 0,asyncRetries = 0; retries < MAXRETRIES; retries++ ) {
- /* make I/O relative to the beginning of the partition */
- gioPB = iopb; /* Remember the parameter block for callback routine */
-
- /* Save the parameters in case we have to retry */
- originalBuffer = (unsigned char *)iopb->ioBuffer;
- originalLength = numblks;
- originalBlock = startblk + pvptr->curoffset;
- originalDir = wrflag;
-
- /* Don't go async (don't call IODone) if call is immed or forceSync is set */
- if( (iopb->ioTrap & IMMED) || (forceSync) )
- err = BlockSCSIXfer( wrflag, numblks, startblk + pvptr->curoffset,(unsigned char *)iopb->ioBuffer,0);
- else
- err = BlockSCSIXfer( wrflag, numblks, startblk + pvptr->curoffset,(unsigned char *)iopb->ioBuffer, (CallbackProc)drvrCompIO);
-
- if( err == 1 )
- return(err); /* Asynchronous Operation, no result yet */
-
- if( err == noErr ) {
- success = 1;
- break;
- }
- /* Must have an error so try again */
-
- }
- if( success )
- dce->dCtlPosition += (iopb->ioActCount = iopb->ioReqCount);
- else {
- /* Bummers */
- err = ioErr;
- iopb->ioActCount = 0; /* report all or none */
- }
- }
- return(err);
- }
-
- /*
- ** This is the throttle point for the old and new interfaces
- */
- short
- BlockSCSIXfer(
- unsigned long wrflag,
- unsigned long count,
- unsigned long sector,
- unsigned char *buf,
- CallbackProc compRoutine ) {
-
- if( newSCSIPresent )
- return( doNewSCSI(wrflag,count,sector,buf,compRoutine) );
-
- return( blockio(wrflag,count,sector,buf) );
-
- }
-
- /*
- ** doNewSCSI performs a SCSI request using the new interface. It
- ** can be either synchronous or asynchronous depending on whether
- ** it is passed a completion routine or not.
- */
- OSErr
- doNewSCSI(
- unsigned long wrflag,
- unsigned long count,
- unsigned long sector,
- unsigned char *buf,
- CallbackProc compRoutine ) {
-
- unsigned char * cmd;
-
- /*
- ** ATTENTION! WAY, WAY IMPORTANT!
- **
- ** This routine can be asynchronous! If we are operating asynchronously
- ** we need to do additional chunks from the completion routine. It is
- ** NOT kosher to sit in a loop here and do multiple commands. We need
- ** to give up our context or we are not asynchronous. Worse yet the
- ** completion routine might re-use the parameter block and/or call
- ** ioDone. The variables thisTime and remaining are globals so that
- ** the completion routine can use them.
- **
- ** Sitting in a loop in the prime routine doing multiple transactions is
- ** the number one most popular way to screw up an asynchronous driver.
- **
- ** If there is no completion routine then we loop until the entire transaction
- ** is done.
- */
- for( ; ; ) { /* Loop is only valid for synchronous requests */
- thisTime = count & 0xFFFF; /* 16 bits of count in a 10 byte command */
- if( !thisTime )
- thisTime = 0x10000; /* 0 in a transfer count == 32 MB */
- remaining = count - thisTime;
-
- cmd = (unsigned char *)&scPB->scsiCDB;
-
- cmd[0] = (wrflag ? 0x2a : 0x28);
- cmd[1] = 0; /* !!! Add support for luns? */
-
- cmd[2] = sector>>24;
- cmd[3] = sector>>16;
- cmd[4] = sector>>8;
- cmd[5] = sector;
-
- cmd[6] = 0; /* reserved */
-
- cmd[7] = count>>8;
- cmd[8] = count;
- cmd[9] = 0; /* reserved */
-
- /* Poll at the end of each block */
- scPB->scsiHandshake[0] = 512;
- scPB->scsiHandshake[1] = 0;
-
- scPB->scsiFlags = standardFlags;
- scPB->scsiIOFlags = standardIOFlags;
-
- if( initiateSyncData) {
- scPB->scsiFlags |= scsiInitiateSyncData;
- initiateSyncData = false;
- }
-
- if( wrflag )
- scPB->scsiFlags |= scsiDirectionOut;
- else
- scPB->scsiFlags |= scsiDirectionIn;
- scPB->scsiFlags |= scsiSIMQNoFreeze;
- scPB->scsiDevice = devIdent;
- scPB->scsiFunctionCode = SCSIExecIO;
- scPB->scsiCDBLength = 10;
-
- scPB->scsiDataPtr = buf;
- scPB->scsiDataLength = count << 9; /* Assumes 512 bytes/sector */
- scPB->scsiDataType = scsiDataBuffer;
- scPB->scsiTransferType = scsiTransferBlind;
-
- if( compRoutine ) {
- /*
- ** Asynchronous
- ** VERY IMPORTANT: We ALWAYS return from an asynchronous request!
- ** Async requests ALWAYS give up their context. We don't ever want
- ** to run through the loop again!
- */
- scPB->scsiCompletion = compRoutine;
- if( SCSIAction( (SCSI_PB *) scPB) )
- return(CMD_ERR); /* Same convention as scsi_op */
- else
- return(1); /* Magic return value! - Tells assembly lang stub it shouldn't call IODone */
- }
- else {
- /*
- ** Synchronous
- ** This section doesn't return if there was no error but instead
- ** will keep looping in the loop.
- */
- scPB->scsiCompletion = nil;
-
- SCSIAction( (SCSI_PB *) scPB);
-
- if( scPB->scsiSCSIstatus == 2 )
- return(CHK_CND); /* Try to use same convention as scsi_op */
-
- if( scPB->scsiResult )
- return(CMD_ERR); /* Try to use same convention as scsi_op */
-
- if( !remaining )
- break;
- count = remaining;
- sector += thisTime;
- buf += (thisTime << 9);
-
- }
- }
- return(noErr);
- }
-
-
- /*
- ** This is the asynchronous completion routine. It assumes that the driver private
- ** storage has been initialized with a "per drive" pointer from the globals area.
- ** This gives it access to the parameter block and dce.
- */
- OSErr
- completeIO( SCSI_IO *scPB )
- {
- IOParam *iopb;
- AuxDCE *dce;
-
- iopb = gioPB;
- dce = gdce;
- if( scPB->scsiResult != noErr || scPB->scsiSCSIstatus != 0 ) {
-
- if( asyncRetries++ > MAXRETRIES )
- return( ioErr );
- else {
- if( doNewSCSI(originalDir,originalLength,originalBlock,originalBuffer, (CallbackProc)drvrCompIO) )
- return(ioErr); /* Not much else we can do here */
- else
- return(1); /* Magic return value - don't call ioDone! */
- }
- }
-
- /*
- ** If we didn't transfer the whole amount with the first tranasaction
- ** will keep calling doNewSCSI until we have used up the transfer count.
- **
- ** Note that the error handling is independent for each chunk we move.
- */
- if( remaining ) {
- originalLength -= thisTime;
- originalBlock += thisTime;
- originalBuffer += (thisTime << 9);
- if( doNewSCSI(originalDir,originalLength,originalBlock,originalBuffer, (CallbackProc)drvrCompIO) )
- return(ioErr); /* Not much else we can do here */
- else
- return(1); /* We still aren't done yet */
- }
-
- dce->dCtlPosition += (iopb->ioActCount = iopb->ioReqCount);
- return(noErr);
- }
-
- /* This function returns the dce from a SCSI parameter block */
- Ptr
- getDCE( SCSI_IO *scPB )
- {
- #pragma unused(scPB);
- return( (Ptr)gdce );
- }
-
-
- /*
- * The control routine...
- */
-
- #define verifyCode 5
- #define formatCode 6
- #define ejectCode 7
- #define iconCode 21
- #define accRun 65
-
-
- OSErr
- control(AuxDCE *dce, CntrlParam *iopb)
- {
-
- OSErr err;
- short i;
- unsigned char *itmp, *icon_name, *geticon(), *getProDOSIcon();
- register struct pervolume *pvptr;
- SCSIBusInquiryPB inqPB; /* Info about the scsi bus */
- SCSIGetVirtualIDInfoPB dIdent;
-
- err = 0;
-
- /*
- ** For accRun calls, we need to reexamine the SCSIMgr configuration to see if the
- ** new SCSI Manager has appeared (only if we are currently using the old SCSI Mgr).
- ** If we were already using the new API or if we are starting to use it now, we
- ** need to examine the capabilities of the bus that we're on to see what feature
- ** flags we can set in our SCSI_IO requests.
- */
- if( iopb->csCode == accRun) { /* one-time configuration */
-
- /* if we weren't using the new API (4.3), see if it's there now */
-
- if( !newSCSIPresent ) {
- if(TrapAvailable(SCSIATOM)) { /* 05/27 MM Use Inside Mac test */
- /* The trap is present and safe to use */
-
- Clear((char *) &dIdent, sizeof(dIdent));
- dIdent.scsiOldCallID = scsiID;
- dIdent.scsiFunctionCode = SCSIGetVirtualIDInfo;
- dIdent.scsiCompletion = nil;
- dIdent.scsiPBLength = sizeof(SCSIGetVirtualIDInfoPB);
- SCSIAction((SCSI_PB *) &dIdent);
-
- if( dIdent.scsiExists ) {
-
- Clear((char *) &inqPB, sizeof(inqPB));
- devIdent = dIdent.scsiDevice;
- inqPB.scsiDevice = devIdent;
- inqPB.scsiFlags = 0;
- inqPB.scsiFunctionCode = SCSIBusInquiry;
- inqPB.scsiCompletion = nil;
- inqPB.scsiPBLength = sizeof(SCSIBusInquiryPB);
- SCSIAction( (SCSI_PB *) &inqPB ); /* Get the size of a SCSI_IO for this bus */
-
- if( inqPB.scsiResult == noErr && inqPB.scsiVersionNumber == scsiVERSION ) {
- if( (scPB = (SCSI_IO *)NewPtrSysClear(inqPB.scsiIOpbSize)) != 0 ) {
- newSCSIPresent = true;
- registerDriver(devIdent,~(scsiID + 32)); /* Announce ourselves */
- NewSCSIPB( inqPB.scsiIOpbSize);
- }
- }
- }
- }
- }
-
- if( newSCSIPresent)
- ConfigureForBusOptions();
-
- for( i = 0; i < MAXPARTITIONS; i++) {
- pvptr = &pv[i];
- if( (pvptr->mydrvnum != NODRVNUM) && pvptr->mountthispart )
- PostEvent(diskEvt, pvptr->mydrvnum); /* System may have forgotten about us */
- }
- /* finished the one-time configuration, so disable dNeedTime */
- dce->dCtlFlags &= 0x4FFF;
- goto done;
- }
-
-
-
- /* For all other csCodes, we first must set up the pvptr */
-
- /* Search for the drive number and set up pvptr to speed up the array access. */
- for( i = 0; i < MAXPARTITIONS; i++) {
- pvptr = &pv[i];
- if( pvptr->mydrvnum == iopb->ioVRefNum )
- break;
- }
-
- /* Make sure that we have a valid drive number. */
- if( i == MAXPARTITIONS) {
- err = nsDrvErr;
-
- } else {
- switch(iopb->csCode) {
-
- case verifyCode:
- case formatCode: /* just pretend it worked */
- break;
-
- case iconCode:
- itmp = geticon();
- /* little hack to show the SCSI ID and partition for this drive */
- BlockMove(&itmp, iopb->csParam, sizeof(itmp));
-
- if( newSCSIPresent )
- {
- Clear((char *) &inqPB, sizeof(inqPB));
- inqPB.scsiDevice = devIdent;
- inqPB.scsiFlags = 0;
- inqPB.scsiFunctionCode = SCSIBusInquiry;
- inqPB.scsiCompletion = nil;
- inqPB.scsiPBLength = sizeof(SCSIBusInquiryPB);
- SCSIAction( (SCSI_PB *) &inqPB ); /* Find out how many buses we have */
-
- if( inqPB.scsiHiBusID == 0 ) {
- icon_name = "\pSCSI ID ? (a)";
- icon_name[ 9] = '0' + devIdent.targetID;
- }
- else {
- icon_name = "\pSCSI ID ?, Bus ? (a)";
- icon_name[ 9] = '0' + devIdent.targetID % 8;
- if( devIdent.bus > 9) {
- icon_name[16] = '0' + devIdent.bus / 10;
- icon_name[17] = '0' + devIdent.bus % 10;
- }
- else
- icon_name[16] = '0' + devIdent.bus;
- }
- }
- else { /* Old API */
- icon_name = "\pSCSI ID ?";
- icon_name[9] = '0' + scsiID;
- }
- BlockMove(icon_name, itmp + 256, icon_name[0] + 1); /* we reserved 32 bytes for strings!!! */
-
- break;
-
- case ejectCode:
- /*
- ** Old (System 4.1?) SFGetFiles will eject disks marked as not ejectable (like ours)
- ** If this happens we need to issue a disk insert post event to re-mount the disk.
- */
- PostEvent(diskEvt, pvptr->mydrvnum);
- /* fall through to default */
-
- default:
- err = controlErr;
- break;
- }
- }
- done:
- return(err);
- }
-
-
- /* Byte to BCD Routine... */
- unsigned char
- ByteToBCD( unsigned char c )
- {
- return( (((c) / 10) << 4) | ((c) % 10) );
- }
-
-
- /*
- * The status routine...
- */
-
- #define drvStsCode 8
- #define DRVSTSSIZE 22 /* csParam holds 22 bytes */
-
- OSErr
- status( AuxDCE * dce, CntrlParam * ioPtr)
- {
- register struct pervolume *pvptr;
- OSErr err = noErr;
- short i;
- DriverGestaltSyncResponse syncResponse;
- DriverGestaltBootResponse bootResponse;
- DriverGestaltVersResponse versResponse;
- #pragma unused (dce)
-
- /* Search for the drive number and set up pvptr to speed up the array access. */
-
- for( i = 0; i < MAXPARTITIONS; i++) {
- pvptr = &pv[i];
- if( pvptr->mydrvnum == ioPtr->ioVRefNum )
- break;
- }
-
- /* Make sure that we have a valid drive number. */
- if( i == MAXPARTITIONS) {
- err = nsDrvErr;
-
- } else
- {
- switch(ioPtr->csCode)
- {
- case drvStsCode:
- BlockMove(&pvptr->track, ioPtr->csParam, DRVSTSSIZE);
- break;
-
- case csDriverGestaltCode:
-
- switch ( ((DriverGestaltParam *)ioPtr)->driverGestaltSelector )
- {
- case driverGestaltSync:
- if( newSCSIPresent )
- syncResponse.behavesSynchronously = false;
- else
- syncResponse.behavesSynchronously = true;
-
- ((DriverGestaltParam *)ioPtr)->driverGestaltResponse = *(unsigned long *)&syncResponse;
- break;
-
- case driverGestaltBoot:
- if( newSCSIPresent ) {
- bootResponse.extDev = dce->dCtlExtDev;
- bootResponse.partition = i;
- bootResponse.SIMSlot = dce->dCtlSlot;
- bootResponse.SIMsRSRC = dce->dCtlSlotId;
-
- ((DriverGestaltParam *)ioPtr)->driverGestaltResponse = *(unsigned long *)&bootResponse;
- }
- else
- err = statusErr;
-
- break;
-
- case driverGestaltVersion:
- versResponse.driverVersion.majorRev = ByteToBCD(VersionMajor);
- versResponse.driverVersion.minorAndBugRev = ByteToBCD(VersionMinor & 0xf);
- versResponse.driverVersion.stage = VersionLevel;
- versResponse.driverVersion.nonRelRev = ByteToBCD(VersionStage);
-
- ((DriverGestaltParam *)ioPtr)->driverGestaltResponse = *(unsigned long *)&versResponse;
- break;
-
- default:
- err = statusErr;
- break;
- }
- break;
-
- default:
- err = statusErr;
- break;
- }
- }
- return(err);
- }
-
-
-
-
- /*
- * This routine will transfer the necessary data. It will break the
- * transfers up into smaller chunks if necessary.
- */
- OSErr
- blockio(unsigned short wrflag, unsigned long count, unsigned long sector, unsigned char *buf)
- {
- unsigned long nb;
- OSErr err;
- unsigned char cmd[10];
-
- initcommand(cmd,wrflag ? 0x2A : 0x28,10);
-
- /*
- ** This is a strictly synchronous routine so it is kosher
- ** to sit in a loop while we do multiple commands.
- */
- while (count > 0) {
- nb = count & 0xFFFF; /* 16 bits of count in a 10 byte command */
- if( !nb )
- nb = 0x10000; /* 0 in a transfer count == 32 MB */
-
- tib[0].scParam1 = (unsigned long)buf; /* scInc opcode does the rest */
- tib[1].scParam2 = nb = count;
-
- cmd[1] = 0; /* !!! Add support for luns? */
-
- cmd[2] = sector>>24;
- cmd[3] = sector>>16;
- cmd[4] = sector>>8;
- cmd[5] = sector;
-
- cmd[6] = 0; /* reserved */
- cmd[9] = 0; /* reserved */
-
- cmd[7] = nb>>8;
- cmd[8] = nb;
- err = scsiop((char *)cmd, 10, tib, wrflag, true, 60*180);
- if( err )
- return( err );
-
- count -= nb;
- sector += nb;
- /* The TIB automagically gets updated */
- }
-
- return(0);
- }
-
-
- /*
- * A generic 'send command to controller' routine. cmd is command block,
- * cmdlen is the command length in bytes, id is SCSI id, tib is the
- * TIB pointer, wr is a write flag, blind is a flag
- * indicating blind or polled reads and writes, and time is the time in
- * ticks to wait for completion.
- */
-
- short
- scsiop(char *cmd, unsigned short cmdlen, struct SCSIInstr *op_tib, unsigned short wr, unsigned short blind, unsigned long time)
- {
- short err, st, msg;
- short attempts;
- OSErr scErr;
-
- err = 0;
-
- for( attempts = 0; attempts < 4; attempts++ ) {
- if( (scErr = SCSIGet()) != noErr ) { /* arbitrate */
-
- while (SCSIStat() & 0x40) /* wait for BSY to go away */
- ;
- }
- else
- break;
- }
- if( scErr )
- return( ARB_ERR );
-
- if( SCSISelect(scsiID)) /* select */
- return(SEL_ERR);
-
- if( SCSICmd(cmd, cmdlen) ) /* send command */
- err = CMD_ERR;
-
- if( !err && op_tib) { /* send tib */
- if( blind) {
- err = wr ? SCSIWBlind((Ptr)op_tib) : SCSIRBlind((Ptr)op_tib);
- } else {
- err = wr ? SCSIWrite((Ptr)op_tib) : SCSIRead((Ptr)op_tib);
- }
- }
-
- if( SCSIComplete(&st, &msg, time)) /* get completion */
- return(COMPL_ERR);
-
- if( err && st != 2) /* report an error unless status is check condition */
- return(err);
-
- return(st);
- }
-
-
- /*
- * This is a routine to initialize scsi commands.
- *
- * cmdbuffer - pointer to buffer
- * cmd - command to load in byte 0
- * len - size of buffer
- *
- */
- void
- initcommand(unsigned char cmdbuffer[], unsigned char cmd, short len)
-
- {
- short index;
-
- for( index = (len - 1); index >= 0 ; index--)
- cmdbuffer[index] = 0;
-
- cmdbuffer[0] = cmd;
- }
-
- void
- Clear( char * where, short len)
- {
- for( ; len>0; len--)
- *where++ = 0;
-
- }
-
-
- /*
- * Test for the presence of the SCSI Manager 4.3 trap using the
- * standard algorithm.
- */
- /*
- * TrapAvailable (see Inside Mac VI 3-8)
- */
- #define NumToolboxTraps() ( \
- (NGetTrapAddress(_InitGraf, ToolTrap) \
- == NGetTrapAddress(0xAA6E, ToolTrap)) \
- ? 0x200 : 0x400 \
- )
- #define GetTrapType(theTrap) ( \
- (((theTrap) & 0x800) != 0) ? ToolTrap : OSTrap \
- )
-
- Boolean
- TrapAvailable(
- short theTrap
- )
- {
- TrapType trapType;
-
- trapType = GetTrapType(theTrap);
- if (trapType == ToolTrap) {
- theTrap &= 0x07FF;
- if (theTrap >= NumToolboxTraps())
- theTrap = _Unimplemented;
- }
- return (
- NGetTrapAddress(theTrap, trapType)
- != NGetTrapAddress(_Unimplemented, ToolTrap)
- );
- }
-
-
-